package service;


import com.distribute.pojo.PageInfo;
import com.distribute.pojo.Result;
import com.distribute.pojo.enums.*;
import com.distribute.pojo.workflow.WorkflowDefinition;
import com.distribute.pojo.workflow.WorkflowScheduler;
import com.distribute.pojo.workflow.WorkflowExecutionTypeEnum;
import com.distribute.utils.JsonUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import constant.DSConstants;
import constant.MediaTypeConstants;
import core.api.ApiClient;
import exception.ApiException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import service.api.WorkflowService;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.distribute.pojo.enums.ReleaseState.OFFLINE;


public class DSWorkflowService implements WorkflowService {

    // 日志
    private static final Logger logger = LoggerFactory.getLogger(DSWorkflowService.class);

    private final ApiClient apiClient;

    public DSWorkflowService(ApiClient apiClient) {
        this.apiClient = apiClient;
    }

    @Override
    public Boolean verifyWorkflowName(Long projectCode, String name) {
        if (name == null || name.isEmpty()) {
            throw new ApiException("name is null");
        }
        Result result = apiClient.get(DSConstants.WORKFLOW_VERIFY_NAME_PATH, Result.class, projectCode, name.trim());
        if (result.isFailed()) {
            throw new ApiException(result.getMsg());
        }
        return result.isSuccess();
    }

    /**
     * 查询工作流定义
     *
     * @param searchVal 工作流名称
     * @return
     */
    @Override
    public WorkflowDefinition getWorkflowDefinition(Long projectCode, String searchVal) {
        if (searchVal == null || projectCode == null) {
            return null;
        }
        Result result = apiClient.get(DSConstants.WORKFLOW_QUERY_PATH,
                Result.class, projectCode, 10, 1, searchVal);

        if (result.isFailed()) {
            throw new ApiException(result.getMsg());
        }

        PageInfo pageInfo = JsonUtils.toBean(result.getData(), PageInfo.class);

        String content = JsonUtils.converString(pageInfo.getTotalList());
        List<WorkflowDefinition> workFlowDefinitions = JsonUtils.toList(content, new TypeReference<List<WorkflowDefinition>>() {
        });

        if (workFlowDefinitions == null || workFlowDefinitions.isEmpty()) {
            return null;
        }
        return workFlowDefinitions.get(0);
    }

    @Override
    public WorkflowDefinition createWorkflowDefinition(Long projectCode, String taskDefinitionJson, String taskRelationJson,
                                                       String locations, String name, String tenantCode,
                                                       WorkflowExecutionTypeEnum executionType, String description,
                                                       Object globalParams, Integer timeout, ReleaseState releaseState) {
        verifyWorkflowName(projectCode, name);
        logger.info("task definition json:{}", taskDefinitionJson);
        logger.info("task relation json:{}", taskRelationJson);
        logger.info("locations:{}", locations);
        logger.info("name:{}", name);
        logger.info("tenant code:{}", tenantCode);
        logger.info("execution type:{}", executionType);
        logger.info("description:{}", description);
        logger.info("global params:{}", globalParams);
        logger.info("timeout:{}", timeout);
        logger.info("release state:{}", releaseState);


        Map<String, Object> body = new HashMap<>();
        body.put(DSConstants.TASK_DEFINITION_JSON, taskDefinitionJson);
        body.put(DSConstants.TASK_RELATION_JSON, taskRelationJson);
        body.put(DSConstants.LOCATIONS, locations);
        body.put(DSConstants.NAME, name);
        body.put(DSConstants.TENANT_CODE, tenantCode);
        body.put(DSConstants.WORKFLOW_EXECUTION_TYPE, executionType);
        body.put(DSConstants.DESCRIPTION, description);
        body.put(DSConstants.GLOBAL_PARAMS, globalParams);
        body.put(DSConstants.TIMEOUT, timeout);
        if (releaseState != null || !OFFLINE.equals(releaseState)) {
            body.put(DSConstants.RELEASE_STATE, releaseState);
        }
        Result result = apiClient.post(DSConstants.WORKFLOW_CREATE_PATH, body,
                MediaTypeConstants.MEDIA_TYPE_FORM, Result.class, projectCode);
        if (result.isFailed()) {
            throw new ApiException(result.getMsg());
        }
        return JsonUtils.toBean(result.getData(), WorkflowDefinition.class);
    }

    @Override
    public Boolean updateWorkflowStatus(Long projectCode, String name, ReleaseState releaseState) {
        // 判断两个入参是否合法
        if (name == null || name.isEmpty() || releaseState == null) {
            throw new ApiException("name or releaseState is null");
        }

        WorkflowDefinition workFlowDefinition = getWorkflowDefinition(projectCode, name);

        Map<String, Object> body = new HashMap<>();
        body.put(DSConstants.RELEASE_STATE, releaseState);
        body.put(DSConstants.NAME, name);
        Result result = apiClient.post(DSConstants.WORKFLOW_ONLINE_OFFLINE_PATH, body, MediaTypeConstants.MEDIA_TYPE_FORM,
                Result.class, projectCode, workFlowDefinition.getCode());
        if (result.isFailed()) {
            throw new ApiException(result.getMsg());
        }
        return result.isSuccess();
    }

    @Override
    public WorkflowScheduler createWorkflowScheduler(Long projectCode, String schedule, FailureStrategy failureStrategy,
                                                     WarningType warningType, Priority processInstancePriority,
                                                     int warningGroupId, String workerGroup, int environmentCode,
                                                     Long processDefinitionCode) {
        if (schedule == null || schedule.isEmpty() || failureStrategy == null || warningType == null || processInstancePriority == null) {
            throw new ApiException("schedule or failureStrategy or warningType or processInstancePriority is null");
        }

        Map<String, Object> body = new HashMap<>();
        body.put(DSConstants.SCHEDULE, schedule);
        body.put(DSConstants.FAILURE_STRATEGY, failureStrategy);
        body.put(DSConstants.WARNING_TYPE, warningType);
        body.put(DSConstants.PROCESS_INSTANCE_PRIORITY, processInstancePriority);
        body.put(DSConstants.WARNING_GROUP_ID, warningGroupId);
        body.put(DSConstants.WORKER_GROUP, workerGroup);
        body.put(DSConstants.ENVIRONMENT_CODE, environmentCode);
        body.put(DSConstants.PROCESS_DEFINITION_CODE, processDefinitionCode);
        Result result = apiClient.post(DSConstants.WORKFLOW_SCHEDULE_PATH, body, MediaTypeConstants.MEDIA_TYPE_FORM, Result.class, projectCode);
        if (result.isFailed()) {
            throw new ApiException(result.getMsg());
        }
        return JsonUtils.toBean(result.getData(), WorkflowScheduler.class);
    }

    @Override
    public Boolean startWorkflowInstance(Long projectCode, Long processDefinitionCode, FailureStrategy failureStrategy,
                                         WarningType warningType, Integer warningGroupId, CommandType execType,
                                         List startNodeList, TaskDependType taskDependType, ComplementDependentMode complementDependentMode,
                                         RunMode runMode, Priority processInstancePriority, String workerGroup, int environmentCode,
                                         Map<String, Object> startParams, int expectedParallelismNumber, int dryRun, String scheduleTime) {
        if (processDefinitionCode == null || failureStrategy == null || warningType == null || execType == null) {
            throw new ApiException("processDefinitionCode or failureStrategy or warningType or execType is null");
        }
        Map<String, Object> body = new HashMap<>();
        body.put(DSConstants.PROCESS_DEFINITION_CODE, processDefinitionCode);
        body.put(DSConstants.FAILURE_STRATEGY, failureStrategy);
        body.put(DSConstants.WARNING_TYPE, warningType);
        body.put(DSConstants.WARNING_GROUP_ID, warningGroupId);
        body.put(DSConstants.EXEC_TYPE, execType);
        body.put(DSConstants.START_NODE_LIST, startNodeList);
        body.put(DSConstants.TASK_DEPEND_TYPE, taskDependType);
        body.put(DSConstants.COMPLEMENT_DEPENDENT_MODE, complementDependentMode);
        body.put(DSConstants.RUN_MODE, runMode);
        body.put(DSConstants.PROCESS_INSTANCE_PRIORITY, processInstancePriority);
        body.put(DSConstants.WORKER_GROUP, workerGroup);
        body.put(DSConstants.ENVIRONMENT_CODE, environmentCode);
        body.put(DSConstants.START_PARAMS, startParams);
        body.put(DSConstants.EXPECTED_PARALLELISM_NUMBER, expectedParallelismNumber);
        body.put(DSConstants.DRY_RUN, dryRun);
        body.put(DSConstants.SCHEDULE_TIME, scheduleTime);

        Result result = apiClient.post(DSConstants.WORKFLOW_START_PATH, body, MediaTypeConstants.MEDIA_TYPE_FORM, Result.class, projectCode);
        if (result.isFailed()) {
            throw new ApiException(result.getMsg());
        }
        return result.isSuccess();
    }


    @Override
    public ServiceType getServiceType() {
        return ServiceType.WORKFLOW;
    }
}
